/*
 * Copyright 2006-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.jstester.util;

import java.lang.reflect.Constructor;

/**
 * Provides handy methods to create assertions and errors.<br>
 * If 'org.testng.Assert' can be loaded it will use TestNg features, if that
 * fails and 'org.junit.Assert' can be loaded it will use JUnit4's features, if
 * both fail it will fallback to JUnit 3.x
 * 
 * @author Andres Almiray <aalmiray@users.sourceforge.net>
 */
public final class JsTesterUtils {
   private static boolean isJUnit4Available;
   private static boolean isTestNGAvailable;
   private static JsAssert jsassert;

   static{
      try{
         Class.forName( "org.testng.Assert" );
         jsassert = (JsAssert) Class.forName( "net.sf.jstester.util.TestNGAssert" )
               .newInstance();
         isTestNGAvailable = true;
      }catch( Exception e ){
         try{
            Class.forName( "org.junit.Assert" );
            jsassert = (JsAssert) Class.forName( "net.sf.jstester.util.JUnit4Assert" )
                  .newInstance();
            isJUnit4Available = true;
         }catch( Exception e2 ){
            try{
               jsassert = (JsAssert) Class.forName( "net.sf.jstester.util.JUnitAssert" )
                     .newInstance();
            }catch( Exception e3 ){
               throw new ExceptionInInitializerError(
                     "Couldn't find TestNG, JUnit4 or JUnit3 on classpath" );
            }
         }
      }
   }

   /**
    * Returns an environment specific JsAssert.
    */
   public static JsAssert getAssert() {
      return jsassert;
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError() {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", null );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", null );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", null );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( boolean message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( char message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( double message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( float message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( int message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( long message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( Object message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", String.valueOf( message ) );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", String.valueOf( message ) );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", String.valueOf( message ) );
      }
   }

   /**
    * Returns an environment specific JsAssertionError.
    */
   public static Error newJsAssertionError( String message ) {
      if( isTestNGAvailable ){
         return newJsAssertionError( "TestNGJsAssertionError", message );
      }else if( isJUnit4Available ){
         return newJsAssertionError( "JUnit4JsAssertionError", message );
      }else{
         return newJsAssertionError( "JUnitJsAssertionError", message );
      }
   }

   private static Error newJsAssertionError( String name, String message ) {
      try{
         Class klass = Class.forName( "net.sf.jstester.util." + name );
         if( message != null ){
            Constructor ctr = klass.getConstructor( new Class[] { String.class } );
            return (Error) ctr.newInstance( new Object[] { message } );
         }else{
            Constructor ctr = klass.getConstructor( new Class[0] );
            return (Error) ctr.newInstance( new Object[0] );
         }
      }catch( Exception e ){
         // ignore, let client fail with NPE
      }
      return null;
   }

   private JsTesterUtils() {

   }
}
